import React from 'react';
import ReactDOM from 'react-dom';
import {Provider,connect} from 'react-redux';
import {createStore, combineReducers, applyMiddleware} from 'redux';
import * as sagaEffects from 'redux-saga/effects';
import createSagaMiddleware from 'redux-saga';

import prefixNamespace from './prefixNamespace'

export {connect}

function dva(){
  const app = {
    _models:[],
    _router:null,
    model,
    router,
    start
  }

  const initialReducers = {};

  function model(model/*model options*/){
    const prefixedModel = prefixNamespace(model);
    app._models.push(model);
    return prefixedModel;
  }

  function router(router){
    app._router = router;
  }

  function start(root){
    for (const model of app._models) {
      initialReducers[model.namespace] = getReducer(model);
    }

    const rootReducer = createReducer();
    const sagaMiddleware = createSagaMiddleware();

    //const store = createStore(rootReducer);
    const store = applyMiddleware(sagaMiddleware)(createStore)(rootReducer);

    //sagaMiddleware.run();
    //todo ↑该run 哪个 saga (我们这里没有定义rootSaga, 只在各个model里定义了 effects
    //model.effects里的都要run
    const sagas = getSagas(app);
    sagas.forEach(sagaMiddleware.run);

    ReactDOM.render(
      <Provider store={store}>
        {app._router()}
      </Provider>,
      document.querySelector(root)
    );

    function createReducer(){
      return combineReducers(initialReducers);
    }
  }

  return app;
}

/**
{
  namespace: 'counter1',
  state: {number: 0},
  reducers: {
    //处理器对象的key再加上上面写的命名空间(namespace), 就是组件里你对应需要派发的动作名 (props.dispatch({type: 'counter/add'})
    add(state) {
      return {number: state.number + 1}
    }
  }
}

=== ↑转变成↓ ===
符合 redux 规范的 reducer
 */
function getReducer(model){
  const {state: initialState, reducers} = model;
  const reducer = (state = initialState, action) => {
    const reducer = reducers[action.type]; //action.type = counter1/add
    if(reducer) return reducer(state);
    return state;
  };
  return reducer;
}

function getSagas(app){
  const sagas = [];
  for(const model of app._models){
    sagas.push(getSaga(model.effects,model.namespace));
  }
  return sagas;
}

function getSaga(effects,namespace){
  return function* (){
    for(const key in effects){
      const watcherSaga = getWatcherSaga(key,effects[key],namespace);
      yield sagaEffects.fork(watcherSaga);
    }
  }
}

function getWatcherSaga(key,effect,namespace){
  return function*(){
    //key "counter/asyncAdd"
    //takeEvery的第二个参数是一个saga,
    //这个saga的第一个参数接受一个action ---> {type:"counter/asyncAdd",payload:xxx}
    //yield sagaEffects.takeEvery(key, effect);

    yield sagaEffects.takeEvery(key,function*(action){
      yield effect(action,{
        ...sagaEffects,
        put:(action)=>{
          return sagaEffects.put({...action,type:prefix(action.type,namespace)}); //在model的effects里 你调用put时派发的动作 不用加model前缀(namespace), 我们在这里会自动加上
        }
      });
    });
  }
}

function prefix(actionType,namespace){
  if(!actionType.includes('/')){
    return `${namespace}/${actionType}`;
  }
  return actionType;
}

export default dva;
